package gnss

import (
	"dgo/goutils/utils"
	"fmt"
	"time"
)

type RtcmMsmHeader struct {
	MsgId uint16
	Sys   byte   // 系统
	StaId uint16 // 跟踪站ID
	TOW   uint   // 历元时间 毫秒
	DOW   uint8  // day of week (0..7)
	TOD   int64  // time of day (毫秒)

	Sync           byte // １：该条消息不是该参考站当前历元的结尾；	０：该条消息是该参考站当前历元的结尾；
	IODS           byte //DF409
	TimeS          byte //DF001
	ClkStr         byte
	ClkExt         byte
	Smooth         byte
	SmoothInterval byte // smoothing interval
	SatN           byte
	Sats           [64]byte // GNSS 卫星掩码字   DF394   Msm 数据编码中是64, 都存放在前面的字符中
	SigN           byte
	Sigs           [32]byte // GNSS 信号掩码字
	CellN          byte
	CellMask       [64]byte // GNSS 数据单元掩码字
}

func NewRtcmMsmHeader() *RtcmMsmHeader {
	rval := &RtcmMsmHeader{}
	return rval
}

func ConvertRtcmRawMsmHeader(msmHeader *RtcmMsmHeader, edHeader *RtcmRawMsmHeader) {
	edHeader.MsgID = msmHeader.MsgId
	edHeader.StaID = msmHeader.StaId
	if msmHeader.Sys == SYS_GLO {
		edHeader.EpochT = uint32(msmHeader.DOW)<<27 + uint32(msmHeader.TOD)
	} else if msmHeader.Sys == SYS_BD {
		edHeader.EpochT = uint32(msmHeader.TOW) - 14*1000
	} else {
		edHeader.EpochT = uint32(msmHeader.TOW)
	}

	edHeader.EpochEndFlag = msmHeader.Sync == 0
	edHeader.IODS = msmHeader.IODS
	edHeader.ClkSteering = msmHeader.ClkStr
	edHeader.ExternalClk = msmHeader.ClkExt
	edHeader.SmoothingType = msmHeader.Smooth
	edHeader.SmoothingInterval = msmHeader.SmoothInterval
	edHeader.SatN = msmHeader.SatN
	edHeader.SatMask = 0

	{ // 小端法
		// 0001100000000000011100000000000000000000000000000000000000000000
		u64 := U64_0
		for i := byte(0); i < msmHeader.SatN; i++ {
			satid := msmHeader.Sats[i]   // 卫星序号
			u64 |= U64_1 << (64 - satid) // (0..63)
		}
		edHeader.SatMask = u64
	}

	{ // 小端法
		// 01100001100000000000000000000000
		u32 := U32_0
		for i := byte(0); i < msmHeader.SigN; i++ {
			sn := msmHeader.Sigs[i]   // 序号
			u32 |= U32_1 << (32 - sn) // (0..31)
		}
		edHeader.SignalMask = u32
	}

	edHeader.CellN = msmHeader.CellN
	u64 := U64_0
	for i := byte(0); i < msmHeader.SatN*msmHeader.SigN; i++ {
		idx := msmHeader.CellMask[i] // 卫星序号
		u64 |= U64_1 << (64 - idx)   // (0..63)
		//SetBitU8(edHeader.CellMask[0:], int(i), int(msmHeader.CellMask[i]), 1)
	}
	edHeader.CellMask = u64
	// fmt.Println("cell-Mask:" , GetBitU64(edHeader.CellMask[0:], 0, int(edHeader.SatN * edHeader.SigN)))

}

/*
*
startBitN: 是MsgID的开始位置
解码RTCM中的时间
 1. 解码出tow (DecodeHeader)
 2. DeocodeTime
*/
func DecodeRtcmBufTime(rtcmBuf []byte, startBitN int, navisys byte, refUtc time.Time) time.Time {
	var gpst time.Time
	gpst = refUtc
	if gpst.IsZero() {
		gpst = UTC2GPSTime(time.Now().UTC())
	}

	//this.MsgId = uint16(GetBitU(buf, startBitN, 12))
	startBitN += 12

	//this.StaId = uint16(GetBitU(buf, startBitN, 12))
	startBitN += 12

	if navisys == SYS_BD {
		// tow
		v := GetBitU(rtcmBuf, startBitN, 30)
		tow := uint(v)
		startBitN += 30

		tow += 14 * 1000
		return TOW2Time(gpst, tow)
	} else if navisys == SYS_GLO {
		//this.DOW = GetBitU8(buf, startBitN, 3) // 周内天 DF416 uint3
		startBitN += 3
		tod := int64(GetBitU(rtcmBuf, startBitN, 27)) // DF034 GLONASS 历元时间（tk） GLONASS 历元时间是 GLONASS ICD 定义的系统时间为 UTC（SU）+3.0hours。0-86,400,999 ms
		startBitN += 27
		return GloTOD2Time(gpst, tod)
	} else {
		// tow
		v := GetBitU(rtcmBuf, startBitN, 30)
		tow := uint(v)
		startBitN += 30
		return TOW2Time(gpst, tow)
	}

}

func (this *RtcmMsmHeader) DecodeTime(refUtc time.Time) time.Time {
	var gpst time.Time
	gpst = refUtc
	if gpst.IsZero() {
		gpst = UTC2GPSTime(time.Now().UTC())
	}
	if this.Sys == SYS_GLO {

		dow, sod := this.DOW, this.TOD
		tow := int32(dow)*int32(SECONDS_PER_DAY)*1000 + int32(sod)
		tow -= int32(SECONDS_PER_HOUR * 3 * 1000) // GLONASS 历元时间是 GLONASS ICD 定义的系统时间为 UTC（SU）+3.0hours.
		tow += LEAP_SECONDS * 1000

		if tow < 0 { // 应该是上周尾部 [BUG]
			tow = tow + MILL_SECONDS_PER_WEEK
		} else if tow >= MILL_SECONDS_PER_WEEK {
			tow -= MILL_SECONDS_PER_WEEK
		}

		return TOW2Time(gpst, uint(tow))

		//t := GPSTime2UTC(gpst).Add(10800 * time.Second)   // GLONASS 历元时间是 GLONASS ICD 定义的系统时间为 UTC（SU）+3.0hours.
		//week, tow := ToGPSTime(t)                         // 返回周内秒， 周
		//_, re := math.Modf(float64(tow) / float64(86400)) // 求 1天之内的秒数
		//tod_p := float64(re * 86400)                      // tod_p: second of day
		//tow -= tod_p                                      // 当前的周内秒 - 天内秒
		//
		//tod := float64(this.TOD) * 0.001    // （天内秒） 毫秒 -> 秒
		//if tod < (float64(tod_p) - 43200) { //43200 为 12小时
		//	tod += 86400 // + 一天
		//} else if tod > (float64(tod_p) + 43200) {
		//	tod -= 86400 // + 一天
		//}
		//
		//rtime := GPSToUtcTime(week, tow+tod).Add(-10800 * time.Second)
		//rtime = UTC2GPSTime(rtime)

		//rtime := GloTOD2Time(gpst, this.TOD)
		//utils.Log.Infof("[GLO][%s]:%d", utils.DateTimeString(rtime), this.TOD)
		//return rtime
	} else if this.Sys == SYS_BD {
		rtime := TOW2Time(gpst, this.TOW)
		//utils.Log.Infof("[BD][%s]:%d", utils.DateTimeString(rtime), this.TOW);
		return rtime
	} else if this.Sys == SYS_GPS {
		rtime := TOW2Time(gpst, this.TOW)
		//utils.Log.Infof("[GPS][%s]:%d", utils.DateTimeString(rtime), this.TOW);
		return rtime
	} else {
		return TOW2Time(gpst, this.TOW)
	}
}

func (this *RtcmMsmHeader) Encode(outBuf []byte, startBitN int) int {
	SetBitU32(outBuf, startBitN, 12, uint32(this.MsgId))
	startBitN += 12

	SetBitU32(outBuf, startBitN, 12, uint32(this.StaId))
	startBitN += 12

	if this.Sys == SYS_BD {
		tow := this.TOW - 14*1000
		SetBitU32(outBuf, startBitN, 30, uint32(tow))
		startBitN += 30
	} else if this.Sys == SYS_GLO {
		SetBitU8(outBuf, startBitN, 3, uint8(this.DOW)) // 周内天 DF416 uint3
		startBitN += 3

		SetBitI64(outBuf, startBitN, 27, this.TOD) // DF034 GLONASS 历元时间（tk） GLONASS 历元时间是 GLONASS ICD 定义的系统时间为 UTC（SU）+3.0hours。0-86,400,999 ms
		startBitN += 27
	} else {
		SetBitU32(outBuf, startBitN, 30, uint32(this.TOW))
		startBitN += 30
	}

	SetBitU8(outBuf, startBitN, 1, uint8(this.Sync))
	startBitN += 1

	SetBitU8(outBuf, startBitN, 3, uint8(this.IODS))
	startBitN += 3

	SetBitU8(outBuf, startBitN, 7, uint8(this.TimeS))
	startBitN += 7

	SetBitU8(outBuf, startBitN, 2, uint8(this.ClkStr))
	startBitN += 2

	SetBitU8(outBuf, startBitN, 2, uint8(this.ClkExt))
	startBitN += 2

	SetBitU8(outBuf, startBitN, 1, uint8(this.Smooth))
	startBitN += 1

	SetBitU8(outBuf, startBitN, 3, uint8(this.SmoothInterval))
	startBitN += 3

	// 卫星编码掩码
	for j := 0; j < 64; j++ {
		idx := this.Sats[j]
		if idx > 0 {
			SetBitU8(outBuf, startBitN+int(idx)-1, 1, 1)
		}
	}
	startBitN += 64

	// 信号编码掩码
	for j := 0; j < 32; j++ {
		idx := this.Sigs[j]
		if idx > 0 {
			SetBitU8(outBuf, startBitN+int(idx)-1, 1, 1)
		}
	}
	startBitN += 32

	n := int(this.SigN) * int(this.SatN)
	for j := 0; j < n; j++ {
		u8 := this.CellMask[j]
		SetBitU8(outBuf, startBitN, 1, u8)
		startBitN += 1
	}

	//this.CellN
	//for j := 0; j < n; j++ {
	//	this.CellMask[j] = GetBitU8(buf, startBitN, 1)
	//	startBitN += 1
	//	if this.CellMask[j] == 1 {
	//		this.CellN++
	//	}
	//}

	return startBitN
}

func (this *RtcmMsmHeader) ExtractSignMask() uint32 {
	var m uint32 = 0
	for i := U8_0; i < 32; i++ {
		if this.Sigs[i] == 1 {
			m |= (1 << (31 - i))
		}
	}
	return m
}

func (this *RtcmMsmHeader) String() string {
	var sb utils.BytesBuilder
	sb.AppendStrs(fmt.Sprintf("msgid:%d, satid:%d, sys:%s", this.MsgId, this.StaId, GetNavSysName(this.Sys))).AppendStr("\r\n")
	sb.AppendStrs(fmt.Sprintf("tow:%d, dow:%d, tod:%d\r\n", this.TOW, this.DOW, this.TOD))
	sb.AppendStrs(fmt.Sprintf("Sync:%d, IODS:%d, TimeS:%d, ClkStr:%d, ClkEx:%d\r\n",
		this.Sync, this.IODS, this.TimeS, this.ClkStr, this.ClkExt))
	sb.AppendStrs(fmt.Sprintf("Smooth:%d, SmoothInterval:%d, satN:%d, SigN:%d, CellN %d, signs:%s\r\n",
		this.Smooth, this.SmoothInterval, this.SatN, this.SigN, this.CellN, PrintRtcmInfoFromMask(this.Sys, this.ExtractSignMask())))
	return sb.String()

}

func (this *RtcmMsmHeader) decodeHead_type1001(buf []byte, startBitN int) int {
	this.MsgId = uint16(GetBitU(buf, startBitN, 12))
	startBitN += 12

	this.StaId = uint16(GetBitU(buf, startBitN, 12))
	startBitN += 12

	// tow
	v := GetBitU(buf, startBitN, 30)
	this.TOW = uint(v)
	startBitN += 30

	this.Sync = byte(GetBitU(buf, startBitN, 1))
	startBitN += 1

	this.SatN = byte(GetBitU(buf, startBitN, 5))
	startBitN += 5

	return startBitN
}

func (this *RtcmMsmHeader) DecodeEx(buf []byte, startBitN int) (int, error) {
	this.MsgId = uint16(GetBitU(buf, startBitN, 12)) //DF002
	startBitN += 12

	this.StaId = uint16(GetBitU(buf, startBitN, 12)) // DF003
	startBitN += 12

	if this.Sys == SYS_BD {
		// tow
		v := GetBitU(buf, startBitN, 30)
		this.TOW = uint(v)
		startBitN += 30

		this.TOW += 14 * 1000
	} else if this.Sys == SYS_GLO {
		//v := GetBitU(buf, startBitN, 30)
		this.DOW = GetBitU8(buf, startBitN, 3) // 周内天 DF416 uint3
		startBitN += 3
		this.TOD = int64(GetBitU(buf, startBitN, 27)) // DF034 GLONASS 历元时间（tk） GLONASS 历元时间是 GLONASS ICD 定义的系统时间为 UTC（SU）+3.0hours。0-86,400,999 ms
		startBitN += 27

		// fmt.Println(v, this.DOW, this.TOD)
	} else {
		// tow
		v := GetBitU(buf, startBitN, 30)
		this.TOW = uint(v)
		startBitN += 30
	}

	this.Sync = byte(GetBitU(buf, startBitN, 1))
	startBitN += 1

	this.IODS = byte(GetBitU(buf, startBitN, 3))
	startBitN += 3

	this.TimeS = byte(GetBitU(buf, startBitN, 7))
	startBitN += 7

	// 站钟调整策略标识 DF411
	this.ClkStr = byte(GetBitU(buf, startBitN, 2))
	startBitN += 2

	// 外钟使用情况标识 DF412
	this.ClkExt = byte(GetBitU(buf, startBitN, 2))
	startBitN += 2

	this.Smooth = byte(GetBitU(buf, startBitN, 1))
	startBitN += 1

	this.SmoothInterval = byte(GetBitU(buf, startBitN, 3))
	startBitN += 3

	//fmt.Println("Sat-Mask:" , GetBitU64(buf, startBitN, 64))
	for j := 1; j <= 64; j++ {
		mask := GetBitU(buf, startBitN, 1)
		startBitN += 1

		if mask == 1 {
			this.Sats[this.SatN] = byte(j)
			this.SatN++
		}
	}

	//fmt.Println("SigN-Mask:" , GetBitU64(buf, startBitN, 32))
	for j := 1; j <= 32; j++ {
		mask := GetBitU(buf, startBitN, 1)
		startBitN += 1

		if mask == 1 {
			this.Sigs[this.SigN] = byte(j)
			this.SigN++
		}
	}

	// In case where there are many satellites and signals for a given GNSS, it is the responsibility of the encoding software to ensure
	//   that the “X≤64” rule by dividing the complete observation set among several messages.
	// GNSS Cell Mask DF396
	n := int(this.SigN) * int(this.SatN)

	if n > 64 {
		return -1, fmt.Errorf("[%d] number of GNSS Cell Mask(DF396) greater than 64, nsat=%d, nsig=%d", this.MsgId, this.SatN, this.SigN)
	}

	this.CellN = 0
	//fmt.Println("Cell-Mask:" , GetBitU64(buf, startBitN, n))
	for j := 0; j < n; j++ {
		this.CellMask[j] = GetBitU8(buf, startBitN, 1)
		startBitN += 1
		if this.CellMask[j] == 1 { // 卫星对应的信号 是否有值
			this.CellN++
		}
	}

	return startBitN, nil

}

func (this *RtcmMsmHeader) Decode(buf []byte) int {
	n, e1 := this.DecodeEx(buf, 0)
	if e1 != nil {
		utils.Log.WarnTypef("rtcm", "err:%s", e1.Error())
	}
	return n
}
